/*
 * PopupGuiCore.cpp
 *
 *  Created on: Jun 7, 2012
 *      Author: guillou
 */

#include <PopupGuiCore.hpp>
#include <PopupSetupDialog.hpp>
#include <PopupMessageWidget.hpp>
#include <PopupUtilities.hpp>
#include <PopupThreadWidget.hpp>

using namespace std;
using namespace utilities;
using namespace Popup;
using namespace PopupUtils;

PopupGuiCore* PopupGuiCore::m_instance = 0;

//==============================================================================
// Construction/Destruction
//==============================================================================

PopupGuiCore* PopupGuiCore::instance()
{
  if (m_instance == 0) {
    m_instance = new PopupGuiCore();
  }
  return m_instance;
}

PopupGuiCore::PopupGuiCore()
: m_settings(PopupSettings::instance()),
  m_sticon(new QSystemTrayIcon(&m_messageDlg)),
  m_clientThread(new PopupClientThread()),
  m_connected(false),
  m_usersManager(PopupUsersManager::instance()),
  m_updateManager(0)
{
  qRegisterMetaType<Popup::UserID>("Popup::UserID");
  qRegisterMetaType<Popup::UserMessage>("Popup::UserMessage");
  qRegisterMetaType<Popup::Canvass>("Popup::Canvass");
  qRegisterMetaType<Popup::Vote>("Popup::Vote");
  qRegisterMetaType<Popup::User>("Popup::User");
  qRegisterMetaType<Popup::ConnectionEcode>("Popup::ConnectionEcode");
  qRegisterMetaType<Popup::FileTransfer>("Popup::FileTransfer");
  qRegisterMetaType<Popup::RateUserMessage>("Popup::RateUserMessage");
  qRegisterMetaType<Popup::UserStatistics>("Popup::UserStatistics");
  qRegisterMetaType<Popup::SessionID>("Popup::SessionID");
  qRegisterMetaType<Popup::ThreadInvitation>("Popup::ThreadInvitation");
  qRegisterMetaType<PopupGui::DialogID>("PopupGui::DialogID");
  qRegisterMetaType<PopupGui::PopupThreadSave>("PopupGui::PopupThreadSave");

  connectCoreAndSystray();
  connectCoreAndClientThread();
  connectCoreAndSetupDialog();
  connectUsersManagerAndClientThread();
  connectMessageDialogAndClientThread();
  connectMessageDialogAndSetupDialog();
  connectMessageDialogAndImageViewer();
  connectMessageDialogAndCore();
  connectLoginDialogAndClientThread();
  connectSetupDialogAndClientThread();

  if (!createDirectory(m_settings->temporaryDirectory().toStdString())) {
    error("Failed to create temporary directory: %s",
          m_settings->temporaryDirectory().toStdString().c_str());
  }
  if (!createDirectory(m_settings->resourceDirectory().toStdString())) {
    error("Failed to create resource directory: %s",
          m_settings->resourceDirectory().toStdString().c_str());
  }

  // Launch client thread...
  m_clientThread->start();
  emit signal_setConnectionParams(m_settings->login(),
                                  m_settings->password(),
                                  m_settings->serverName(),
                                  m_settings->serverPort(),
                                  false);

  // Call settings update slot. This will handle all operations
  // which are settings dependent: update management thread,
  // update of systray icon, ...
  slot_onSettingsUpdated();

  PopupUsersManager::instance()->registerObserver(this);
}

void PopupGuiCore::connectCoreAndSystray()
{
  connect(m_sticon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
          this, SLOT(slot_iconActivated(QSystemTrayIcon::ActivationReason)));
}

void PopupGuiCore::connectCoreAndUpdateManagerThread()
{
  connect(m_updateManager, SIGNAL(signal_updateAvailable(float, const QString &)),
          this, SLOT(slot_onUpdateAvailable(float, const QString &)));
  connect(m_updateManager, SIGNAL(finished()),
          this, SLOT(slot_onUpdateThreadFinished()));
  connect(m_updateManager, SIGNAL(signal_updateFailed()),
          this, SLOT(slot_onUpdateFailed()));

  connect(this, SIGNAL(signal_killUpdateManagerThread()),
          m_updateManager, SLOT(slot_exit()));
  connect(this, SIGNAL(signal_launchUpdate()),
          m_updateManager, SLOT(slot_launchUpdate()));

  connect(m_clientThread, SIGNAL(signal_onConnectionUpdate(bool, Popup::User, Popup::SessionID)),
          m_updateManager, SLOT(slot_onConnectionUpdate(bool)));
}

void PopupGuiCore::connectCoreAndClientThread()
{
  connect(m_clientThread, SIGNAL(signal_onConnectionUpdate(bool, Popup::User, Popup::SessionID)),
          this, SLOT(slot_onConnectionUpdate(bool)));

  connect(this, SIGNAL(signal_setConnectionParams(const QString &, const QString, const QString &, int, bool)),
          m_clientThread, SLOT(slot_setConnectionParams(const QString &, const QString, const QString &, int, bool)));
  connect(this, SIGNAL(signal_disconnectClient()),
          m_clientThread, SLOT(slot_disconnect()));
}

void PopupGuiCore::connectCoreAndSetupDialog()
{
  connect(&m_setupDlg, SIGNAL(signal_applyNewSettings()),
          this, SLOT(slot_onSettingsUpdated()));
}

void PopupGuiCore::connectUsersManagerAndClientThread()
{

  connect(m_clientThread, SIGNAL(signal_updateUser(Popup::User, unsigned short, bool)),
          m_usersManager, SLOT(slot_updateUser(Popup::User, unsigned short, bool)));
  connect(m_clientThread, SIGNAL(signal_onConnectionUpdate(bool, Popup::User, Popup::SessionID)),
          m_usersManager, SLOT(slot_onConnectionUpdate(bool, Popup::User, Popup::SessionID)));
  connect(m_clientThread, SIGNAL(signal_updateUserStatistics(Popup::UserID, Popup::UserStatistics)),
          m_usersManager, SLOT(slot_updateUserStatistics(Popup::UserID, Popup::UserStatistics)));

  connect(m_usersManager, SIGNAL(signal_localUserUpdated(const Popup::User*, unsigned short)),
          m_clientThread, SLOT(slot_localUserUpdated(const Popup::User*, unsigned short)));
}

void PopupGuiCore::connectMessageDialogAndClientThread()
{
  connect(m_clientThread, SIGNAL(signal_onConnectionUpdate(bool, Popup::User, Popup::SessionID)),
          &m_messageDlg, SLOT(slot_onConnectionUpdate(bool)));

  connect(m_clientThread, SIGNAL(signal_messageReceived(Popup::UserMessage)),
          m_messageDlg.widget(), SLOT(slot_messageReceived(Popup::UserMessage)));
  connect(m_clientThread, SIGNAL(signal_messageSent(Popup::UserMessage)),
          m_messageDlg.widget(), SLOT(slot_messageSent(Popup::UserMessage)));
  connect(m_clientThread, SIGNAL(signal_messageReactionReceived(Popup::RateUserMessage)),
          m_messageDlg.widget(), SLOT(slot_messageReactionReceived(Popup::RateUserMessage)));
  connect(m_clientThread, SIGNAL(signal_onOpinionRequested(Popup::Canvass)),
          m_messageDlg.widget(), SLOT(slot_onOpinionRequested(Popup::Canvass)));
  connect(m_clientThread, SIGNAL(signal_onVoteReceived(Popup::Vote)),
          m_messageDlg.widget(), SLOT(slot_onVoteReceived(Popup::Vote)));
  connect(m_clientThread, SIGNAL(signal_onInvitationReceived(Popup::ThreadInvitation)),
          m_messageDlg.widget(), SLOT(slot_onInvitationReceived(Popup::ThreadInvitation)));

  connect(m_messageDlg.widget(), SIGNAL(signal_sendMessage(Popup::UserMessage*)),
          m_clientThread, SLOT(slot_sendMessage(Popup::UserMessage*)));
  connect(m_messageDlg.widget(), SIGNAL(signal_sendMessageReaction(Popup::RateUserMessage*)),
          m_clientThread, SLOT(slot_sendMessageReaction(Popup::RateUserMessage*)));
  connect(m_messageDlg.widget(), SIGNAL(signal_submitCanvass(Popup::Canvass*)),
          m_clientThread, SLOT(slot_submitCanvass(Popup::Canvass*)));
  connect(m_messageDlg.widget(), SIGNAL(signal_sendVote(Popup::Vote*)),
          m_clientThread, SLOT(slot_sendVote(Popup::Vote*)));
  connect(m_messageDlg.widget(), SIGNAL(signal_sendInvitation(Popup::ThreadInvitation*)),
          m_clientThread, SLOT(slot_sendInvitation(Popup::ThreadInvitation*)));
}

void PopupGuiCore::connectMessageDialogAndSetupDialog()
{
  connect(&m_setupDlg, SIGNAL(signal_applyNewSettings()),
          &m_messageDlg, SLOT(slot_applyNewSettings()));
}

void PopupGuiCore::connectMessageDialogAndCore()
{
  connect(&m_messageDlg, SIGNAL(signal_showDialog(PopupGui::DialogID)),
          this, SLOT(slot_showDialog(PopupGui::DialogID)));
  connect(this, SIGNAL(signal_restoreThread(const PopupGui::PopupThreadSave &)),
          &m_messageDlg, SLOT(slot_restoreThread(const PopupGui::PopupThreadSave &)));
}

void PopupGuiCore::connectMessageDialogAndImageViewer()
{
  connect(m_messageDlg.widget(), SIGNAL(signal_showImage(const QString &, const QString &, bool)),
          &m_imageviewer, SLOT(slot_showImage(const QString &, const QString &, bool)));
}

void PopupGuiCore::connectLoginDialogAndClientThread()
{
  connect(m_clientThread, SIGNAL(signal_badConnectionParams(Popup::ConnectionEcode)),
          &m_loginDlg, SLOT(slot_badConnectionParams(Popup::ConnectionEcode)));

  connect(&m_loginDlg, SIGNAL(signal_restartClient()),
          m_clientThread, SLOT(slot_restartClient()));
  connect(&m_loginDlg, SIGNAL(signal_setConnectionParams(const QString &, const QString, const QString &, int, bool)),
          m_clientThread, SLOT(slot_setConnectionParams(const QString &, const QString,
                                                                                                       const QString &, int, bool)));
}

void PopupGuiCore::connectSetupDialogAndClientThread()
{
  connect(m_clientThread, SIGNAL(signal_updateFileTransferProgress(Popup::FileTransfer)),
          &m_transfersDlg, SLOT(slot_updateFileTransferProgress(Popup::FileTransfer)));
}

//==============================================================================
// Miscellaneous utility members
//==============================================================================

void PopupGuiCore::slot_showError(const QString & p_error)
{
  showError(p_error, &m_messageDlg);
}

void PopupGuiCore::slot_showBalloon(const QString & p_header,
                                    const QString & p_text)
{
  m_sticon->showMessage(p_header, p_text, QSystemTrayIcon::Information, 5000);
}

void PopupGuiCore::slot_onSettingsUpdated()
{
  // Start/stop update manager thread if necessary
  if (m_settings->updateEnabled() && !m_updateManager) {
    m_updateManager = new PopupUpdateManager();
    connectCoreAndUpdateManagerThread();
    m_updateManager->start();
  } else if (!m_settings->updateEnabled() && m_updateManager) {
    emit signal_killUpdateManagerThread();
    // It will be deleted in slot_updateManagerThreadFinished() slot.
    m_updateManager = 0;
  }

  // Update systray icon
  slot_updateSystrayIcon();
}

void PopupGuiCore::localUserUpdated(unsigned short p_updateMask)
{
  if (p_updateMask & POPUP_USER_FIELD_MODE) {
    // Update systray icon
    slot_updateSystrayIcon();  
  }
}

void PopupGuiCore::slot_updateSystrayIcon()
{
  QIcon _icon;
  QString _str("[Popup]\n");

  _str += tr("server: ") + m_settings->serverName() + "\n";

  if (!m_connected) {
    _icon = QIcon(":/images/popup_disconnected.png");
    _str  += tr("Connecting...\n");
  }
  else {
    switch (m_settings->defaultClientMode())
    {
      case POPUP_MODE_TEXTONLY:
        _str  += tr("mode: <text>\n");
        break;
      case POPUP_MODE_PROMPT:
        _str  += tr("mode: <prompt>\n");
        break;
      case POPUP_MODE_MUTED:
        _str  += tr("mode: <quiet>\n");
        break;
      case POPUP_MODE_DEFAULT:
      default:
        _str  += tr("mode: <open>\n");
        break;
    }
    getIconFromMode(_icon,m_settings->defaultClientMode());
  }

  m_messageDlg.setWindowIcon(_icon);
  m_setupDlg.setWindowIcon(_icon);
  m_sticon->setIcon(_icon);
  m_sticon->setToolTip(_str);
  m_sticon->show();
}

//----------------------------------------------------------

void PopupGuiCore::slot_iconActivated(QSystemTrayIcon::ActivationReason p_reason)
{
  PopupSettings::PopupSystrayAction _action = m_settings->systrayAction(p_reason);

  switch (_action)
  {
    case PopupSettings::POPUP_EXIT:
      exit();
      break;
    case PopupSettings::POPUP_SHOW_WINDOW:
      m_messageDlg.setVisible(true);
      break;
    case PopupSettings::POPUP_SWITCH_CLIENT_MODE:
    {
      Popup::UserMode _mode;
      // Shift to next mode
      _mode = getNextMode(m_settings->defaultClientMode());

      // Save settings
      m_settings->setDefaultClientMode(_mode);
      m_settings->serialize(PopupSettings::POPUP_SAVE_TO_FILE);

      // Let everybody know about our new status
      setMode(_mode);
      break;
    }
    case PopupSettings::POPUP_NO_ACTION:
    default:
      break;
  }
}

void PopupGuiCore::slot_onConnectionUpdate(bool p_connected)
{
  m_connected = p_connected;
  static bool firstConnectionDown = true;
  if (p_connected)
  {
    firstConnectionDown = true;
    slot_showBalloon("Popup connected!",
                     " Server:" + m_settings->serverName() +
                     " Port:" + QString::number(m_settings->serverPort()));
    restore();
  }
  else
  {
    if (firstConnectionDown == true) {
      firstConnectionDown = false;
      slot_showBalloon("Popup connecting...",
                       " Server:" + m_settings->serverName() +
                       " Port:" + QString::number(m_settings->serverPort()));
    }
  }
  slot_updateSystrayIcon();
}

void PopupGuiCore::slot_onUpdateAvailable(float p_version,
                                          const QString & p_changes)
{
  if (confirm("Popup version " + QString::number(p_version, 'f', 2) +
              " is available.\nDo you want to install right now?\n\n" +
              "This new version provides the following crap :)\n\n" +
              p_changes))
  {
    emit(signal_disconnectClient());
    emit(signal_launchUpdate());
  }
}

void PopupGuiCore::slot_onUpdateFailed()
{
  // Emit connection params update notification to force client
  // to reconnect
  emit signal_setConnectionParams(m_settings->login(),
                                  m_settings->password(),
                                  m_settings->serverName(),
                                  m_settings->serverPort(),
                                  false);
  // Let user know that I am a crap
  showError("Too bad! Update failed! See logs for details.");
}

void PopupGuiCore::slot_onUpdateThreadFinished()
{
  delete ((PopupUpdateManager*) QObject::sender());
}

void PopupGuiCore::slot_showDialog(PopupGui::DialogID p_dlg)
{
  switch (p_dlg)
  {
    case PopupGui::POPUP_DIALOG_ID_SETTINGS:
      m_setupDlg.setVisible(!m_setupDlg.isVisible());
      break;
    case PopupGui::POPUP_DIALOG_ID_LOGS:
      m_logsDlg.setVisible(!m_logsDlg.isVisible());
      break;
    case PopupGui::POPUP_DIALOG_ID_LOGIN:
      m_loginDlg.setVisible(!m_loginDlg.isVisible());
      break;
    case PopupGui::POPUP_DIALOG_ID_TRANSFERS:
    default:
      break;
  }
}

#define POPUP_SESSION_BACKUP_FILENAME ".session.txt"

void PopupGuiCore::saveSession()
{
  QString _sessionPath = m_settings->resourceDirectory() + "/" +
      POPUP_SESSION_BACKUP_FILENAME;

  QSettings _session(_sessionPath, QSettings::IniFormat);
  trace("Saving settings to %s", _sessionPath.toStdString().c_str());

  int _n = 1;
  vector<const PopupThreadWidget*> _threads;
  m_messageDlg.getThreadWidgets(_threads);
  vector<const PopupThreadWidget*>::iterator _thread;

  _session.beginGroup("Global");
  _session.setValue("msgWidgetVisible", m_messageDlg.isVisible());
  _session.setValue("msgWidgetPos", m_messageDlg.geometry());
  _session.setValue("setupWidgetVisible", m_setupDlg.isVisible());
  _session.setValue("setupWidgetPos", m_setupDlg.geometry());
  _session.setValue("logsWidgetVisible", m_logsDlg.isVisible());
  _session.setValue("logsWidgetPos", m_logsDlg.geometry());
  _session.endGroup();

  for (_thread = _threads.begin(); _thread != _threads.end(); _thread++, _n++)
  {
    _session.beginGroup("Thread_" + QString::number(_n));
    _session.setValue("ID", (*_thread)->getID());
    _session.setValue("Title", (*_thread)->getTitle());
    _session.setValue("Users", (*_thread)->getUsers());
    _session.setValue("Text", (*_thread)->getText());
    if ((*_thread)->isVisible()) {
      _session.setValue("Active", (*_thread)->isActiveWindow());
    }
    _session.endGroup();
  }

  _session.sync();
}

void PopupGuiCore::exit()
{
  saveSession();
  ::exit(0);
}

void PopupGuiCore::restore()
{
  QString _sessionPath = m_settings->resourceDirectory() + "/" +
      POPUP_SESSION_BACKUP_FILENAME;

  if (QFile::exists(_sessionPath))
  {
    QSettings _session(_sessionPath, QSettings::IniFormat);
    int _n = 1;

    QString _group;
    foreach(_group, _session.childGroups())
    {
      if (QString("Thread_") + QString::number(_n) == _group)
      {
        PopupGui::PopupThreadSave _savedThread;
        _savedThread.ID = _session.value(_group + "/ID").toULongLong();
        _savedThread.title = _session.value(_group + "/Title").toString();
        _savedThread.text = _session.value(_group + "/Text").toString();
        _savedThread.isActive = _session.value(_group + "/Active", "false").toBool();
        QString _userIdStr;
        foreach(_userIdStr, _session.value(_group + "/Users").toStringList()) {
          _savedThread.users.insert(_userIdStr.toULong());
        }

        emit signal_restoreThread(_savedThread);

        _n++;
      }
    }

    m_messageDlg.setVisible(_session.value("Global/msgWidgetVisible",
                                           "false").toBool());
    if (_session.contains("Global/msgWidgetPos")) {
      m_messageDlg.setGeometry(_session.value("Global/msgWidgetPos").toRect());
    }
    m_setupDlg.setVisible(_session.value("Global/setupWidgetVisible",
                                         "false").toBool());
    if (_session.contains("Global/setupWidgetPos")) {
      m_setupDlg.setGeometry(_session.value("Global/setupWidgetPos").toRect());
    }
    m_logsDlg.setVisible(_session.value("Global/logsWidgetVisible",
                                        "false").toBool());
    if (_session.contains("Global/logsWidgetPos")) {
      m_logsDlg.setGeometry(_session.value("Global/logsWidgetPos").toRect());
    }

    QFile::remove(_sessionPath);
  }
}

